using System;
using System.Text;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using My.IdlConverter.Core;
using My.IdlConverter.Core.Producers;
using My.IdlConverter.Core.Filters;
using My.IdlConverter.Core.Util;

namespace My.IdlConverter.Com
{
    public delegate bool NeedInitialization(Class classObj); 

    public class ComClassHeaderFileProducer : ClassProducer
    {
        public static readonly string ONE_METHOD_REGEX = @"\[.*\].*\);";
        public static readonly string METHODANDPARAM_PREFIX_REGEX = @"\[.*?\]";
        public NeedInitialization _needInitialization;

        public NeedInitialization NeedInitialization
        {
            get
            {
                return _needInitialization;
            }
            set
            {
                if (value != null)
                    _needInitialization = value;
            }
        }

        public ComClassHeaderFileProducer()
            :this(null)
        {
        }

        public ComClassHeaderFileProducer(Class consumer)
            : base(consumer)
        {
            _needInitialization = DefaultNeedInitialization;
        }

        public override string Produce()
        {
            IProducer<Interface> producer = new ComInterfaceProducer();

            StringBuilder sbClassMethods = new StringBuilder();

            StringBuilder theClassString = new StringBuilder();
            string classTitle = ClassModifier.GetTitle(Consumer);

            ComClassTemplate classTemplate = new ComClassTemplate();
            classTemplate.Build(Consumer);

            theClassString.Append(classTemplate[1].Replace(GlobalConst.TemplatePattern, ClassModifier.GetTitle(Consumer)).ToString());
            theClassString.Append(Generic_INHERIATE_INTERFACE_TEMPLATE().ToString());
            theClassString.Append(classTemplate[3].Replace(GlobalConst.TemplatePattern,classTitle)
                                                  .Replace(GlobalConst.TemplatePatternU, StringUtil.UpperString(classTitle))
                                                  .ToString());
            theClassString.Append(Generic_COM_INTERFACE_ENTRY().ToString());
            theClassString.Append(classTemplate[5].Replace(GlobalConst.TemplatePattern, classTitle).ToString());
            //theClassString.AppendLine("/*");
            theClassString.Append(GetMethodsString(Consumer).ToString());
            //theClassString.AppendLine("*/");

            //theClassString.Append(GenerateInitializationMethods(Consumer));
            //last part
            theClassString.Append(classTemplate[7].Replace(GlobalConst.TemplatePattern, classTitle).ToString());
            theClassString.Replace(GlobalConst.TemplatePattern,ClassModifier.GetTitle(Consumer));

            return theClassString.ToString();
        }

        private string Generic_INHERIATE_INTERFACE_TEMPLATE()
        {
            StringBuilder sb = new StringBuilder();
            string inheritString = null;
            int interfaceCount = Consumer.BaseInterfaces.Count;
            foreach (Interface nextInterface in Consumer.BaseInterfaces)
            {
                if (nextInterface == null)
                {
                    System.Diagnostics.Trace.WriteLine(Consumer.Name);
                    continue;
                }
                if (!nextInterface.IsClass())
                {
                    inheritString =  ComClassTemplate.IDispatchImp.Replace(GlobalConst.TemplatePattern, InterfaceModifier.GetTitle(nextInterface.Name)) + ",";
                }
                else
                {
                    inheritString = ComClassTemplate.ExtendsBaseClass + ClassModifier.GetName(nextInterface.Name) + ",";
                }
                sb.AppendLine(inheritString);
            }
            if (IsNeedInitialization(Consumer))
            {
                inheritString = ComClassTemplate.ExtendsBaseClass + "CInitialization,";
                sb.AppendLine(inheritString);
            }

            string value = sb.ToString().TrimEnd();
            value = value.Remove(value.Length-1,1);
            return value+Environment.NewLine;
        }


        private string Generic_COM_INTERFACE_ENTRY()
        {
            StringBuilder sb = new StringBuilder();

            string comInterfaceEntry = null;

            int interfaceCount = Consumer.BaseInterfaces.Count;
            bool needComInterfaceEntry2 = false;

            foreach (Interface nextInterface in Consumer.BaseInterfaces)
            {
                if (nextInterface == null)
                    continue;
                if (!nextInterface.IsClass())
                {
                    if (nextInterface == null)
                        continue;
                    if (!needComInterfaceEntry2)
                    {
                        comInterfaceEntry = ComClassTemplate.ComInterfaceEntry.Replace(GlobalConst.TemplatePattern, InterfaceModifier.GetTitle(nextInterface.Name));
                        needComInterfaceEntry2 = true;
                    }
                    else
                    {
                        comInterfaceEntry = ComClassTemplate.ComInterfaceEntry2.Replace(GlobalConst.TemplatePattern, InterfaceModifier.GetTitle(nextInterface.Name));
                    }
                }
                else
                {
                    comInterfaceEntry = ComClassTemplate.ComInterfaceEntryChain.Replace(GlobalConst.TemplatePattern, ClassModifier.GetTitle(nextInterface.Name));
                }
                sb.AppendLine(comInterfaceEntry);
            }
            if (IsNeedInitialization(Consumer))
            {
                comInterfaceEntry = ComClassTemplate.ComInterfaceEntryIID.Replace(GlobalConst.TemplatePattern, "Initialization");
                sb.AppendLine(comInterfaceEntry);
            }
            if (sb[sb.Length - 1] == ',')
                sb = sb.Remove(sb.Length-1,1);

            return sb.ToString();
        }

        public StringBuilder GetMethodsString(Class theClass)
        {
            StringBuilder sbMethods = new StringBuilder();

            IProducer<Method> methodProducer = new ComMethodProducer();
            foreach (Method nextMethod in theClass.Methods)
            {
                methodProducer.Consumer = nextMethod;
                sbMethods.AppendLine(ChangeToClassMethod(nextMethod, methodProducer.Produce()));
            }

            return sbMethods;
        }

        private string ChangeToClassMethod(Method theMethod,string interfaceMethod)
        {
            string classMethod = interfaceMethod;

            Regex reg = new Regex(METHODANDPARAM_PREFIX_REGEX);
            MatchCollection methodAndParamPrefix = reg.Matches(interfaceMethod);

            string tempPrefix = null;
            int tempPos = -1;
            foreach (Match nextPrefix in methodAndParamPrefix)
            {
                tempPrefix = nextPrefix.Value;
                tempPos = classMethod.IndexOf(tempPrefix);
                Debug.Assert(tempPos != -1);
                classMethod = classMethod.Remove(tempPos,tempPrefix.Length);
            }

            classMethod = classMethod.Trim();
            classMethod = classMethod.Remove(0, "HRESULT ".Length);
            classMethod = classMethod.Trim();
            if (theMethod.Getter)
                classMethod = "get_" + classMethod;
            if (theMethod.Setter)
                classMethod = "put_" + classMethod;
            classMethod = "	HRESULT STDMETHODCALLTYPE " + classMethod;

            return classMethod;
        }

        /// <summary>
        /// 	
        //virtual void Init(void* /*pIFInterface*/)
	    //{
		//  ATLASSERT(FALSE);
	    //}
	    //virtual const vtl::Cv_PTR& GetWrapperObj() const
	    //{
        //  return m_ptrObj;
	    //}
        /// </summary>
        /// <param name="theClass"></param>
        /// <returns></returns>
        private StringBuilder GenerateInitializationMethods(Class theClass)
        {
            StringBuilder statements = new StringBuilder("");
            if (!theClass.IsFinal())
            {
                return statements;
            }
            IList<Interface> pureInterfaces = new List<Interface>();
            theClass.GetPureBaseInterfaces(ref pureInterfaces);
            foreach (Interface nextInterface in pureInterfaces)
            {
                statements.Append(GenerateOneInitializationMethod(nextInterface));
            }
            Interface theOnlyInterface = pureInterfaces[0];
            string interfacPointer = InterfaceModifier.GetVariable(theOnlyInterface);
            statements.AppendLine(StringUtil.FormatIndent1("virtual void Init(void* pIFInterface)"));
            statements.AppendLine(StringUtil.FormatIndent1("{"));
            statements.AppendLine(StringUtil.FormatIndent2("{0} = ({1})pIFInterface;",interfacPointer, theOnlyInterface.Name + "*"));
            statements.AppendLine(StringUtil.FormatIndent1("}"));
            statements.AppendLine("private:");
            statements.AppendLine(StringUtil.FormatIndent1(theOnlyInterface.Name + "* " + interfacPointer + ";"));

            return statements;
        }

        private StringBuilder GenerateOneInitializationMethod(Interface theInterface)
        {
            StringBuilder statements = new StringBuilder();
            string interfaceTitle = InterfaceModifier.GetTitle(theInterface.Name);
            statements.AppendFormat(StringUtil.FormatIndent1("virtual {0}* GetIFDOM{2}() const", 
                                    theInterface.Name,
                                    interfaceTitle));
            statements.AppendLine();
            statements.AppendLine(StringUtil.FormatIndent1("{"));
            statements.Append(ComInitializeMethodTempalte.Instantiate(GlobalConst.DefaultIndent2, interfaceTitle));
            statements.AppendLine(StringUtil.FormatIndent1("}"));

            return statements;
        }

        private bool IsNeedInitialization(Class classObj)
        {
            if (NeedInitialization == null)
                NeedInitialization = DefaultNeedInitialization;
            return (NeedInitialization(classObj));
        }

        private bool DefaultNeedInitialization(Class classObj)
        {
            if ( (classObj.IsFinal()) && 
                 (classObj.Name.IndexOf("Session")==-1) )
                return true;
            return false;
        }
    }
}
